package com.inspur.demo.controller;

import com.inspur.demo.base.BaseController;
import com.inspur.demo.common.CommResultMsg;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
import org.springframework.cloud.netflix.ribbon.RibbonLoadBalancerClient;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

import javax.annotation.Resource;
import java.net.URI;
import java.net.URISyntaxException;
import java.text.MessageFormat;

/**
 *    
 * 控制层
 *
 * @desc:
 * @author: li.peng02
 * @date: 2019/3/13
 * @version: v1.0 
 */
@RestController
public class RibbonController extends BaseController {

    private Logger logger = LoggerFactory.getLogger(RibbonController.class);

    //如果使用@Autowired注入，默认按照类型注入，由于前面已经使用了@Primary注解，默认注入非负载均衡的bean
    //使用@LoadBalanced指定注入带有负载均衡的bean或者使用@Qualifier("balanceRestTemplate")指定bean名称
    //@Autowired
    //使用@Resource默认按照bean名称注入
    @Resource
    private RestTemplate noBalanceRestTemplate;

    @Resource
    private RestTemplate balanceRestTemplate;

    //由于LoadBalancerClient只有RibbonLoadBalancerClient实现，所以这里两个loadBalancerClient注入的是同一个bean
    @Resource
    private LoadBalancerClient loadBalancerClient;

    @Resource
    private RibbonLoadBalancerClient ribbonLoadBalancerClient;

    /**
     * 使用带有负载均衡的RestTemplate和clientName访问生产者
     *
     * @return
     */
    @RequestMapping("/old/balance/test")
    public ResponseEntity<CommResultMsg> test01() {
        // 如果使用 localhost 访问接口，提示 No instances available for localhost
        ResponseEntity<CommResultMsg> forEntity = balanceRestTemplate.getForEntity("http://spring-cloud-producer/producer/test?name=123", CommResultMsg.class);
        return forEntity;
    }

    /**
     * 使用不带有负载均衡的RestTemplate和IP：PORT访问生产者
     *
     * @return
     */
    @RequestMapping("/old/nobalance/test")
    public ResponseEntity<CommResultMsg> test02() {
        // 如果使用 instanceId 访问接口会抛出异常 java.net.UnknownHostException: spring-cloud-producer
        ResponseEntity<CommResultMsg> forEntity = noBalanceRestTemplate.getForEntity("http://localhost:9903/producer/test?name=123", CommResultMsg.class);
        return forEntity;
    }

    @RequestMapping("/new/balance/test")
    public void test03() throws URISyntaxException {
        //下面俩个client是同一个bean实例，choose表示启用负载均衡后选择的服务实例，默认情况下依次轮训Producer服务器
        logger.info("[loadBalancerClient]choose的结果,代表负载均衡之后要选择的服务实例");
        ServiceInstance instance1 = loadBalancerClient.choose("spring-cloud-producer");
        logger.info(MessageFormat.format("host:{0},port:{1},serviceId={2},uri={3}", instance1.getHost(), instance1.getPort(), instance1.getServiceId(), instance1.getUri()));

        logger.info("[ribbonLoadBalancerClient]choose的结果,代表负载均衡之后要选择的服务实例");
        ServiceInstance instance2 = ribbonLoadBalancerClient.choose("spring-cloud-producer");
        logger.info(MessageFormat.format("host:{0},port:{1},serviceId={2},uri={3}", instance2.getHost(), instance2.getPort(), instance2.getServiceId(), instance2.getUri()));

        try {
            // 根据负载均衡后的服务,构建一个访问url
            // 第二个参数不能为null
            logger.info("根据负载均衡后的服务,构建一个访问url");
            URI reconstructURI = ribbonLoadBalancerClient.reconstructURI(instance1, new URI(""));
            logger.info(MessageFormat.format("reconstructURI1-yes:{0}", reconstructURI));
            // 拼写在请求地址后边，需要注意是否需要添加/
            reconstructURI = ribbonLoadBalancerClient.reconstructURI(instance1, new URI("/producer/test?name=123"));
            logger.info(MessageFormat.format("reconstructURI2-yes:{0}", reconstructURI));
            reconstructURI = ribbonLoadBalancerClient.reconstructURI(instance1, new URI("http"));
            logger.info(MessageFormat.format("reconstructURI3-no:{0}", reconstructURI));
            reconstructURI = ribbonLoadBalancerClient.reconstructURI(instance1, new URI("https"));
            logger.info(MessageFormat.format("reconstructURI4-no:{0}", reconstructURI));
            reconstructURI = ribbonLoadBalancerClient.reconstructURI(instance1, new URI("producer/test?name=123"));
            logger.info(MessageFormat.format("reconstructURI5-no:{0}", reconstructURI));
            // 使用http:/xxx、https:/xxx可以用于切换http协议还是https协议
            reconstructURI = ribbonLoadBalancerClient.reconstructURI(instance1, new URI("http:/producer/test?name=123"));
            logger.info(MessageFormat.format("reconstructURI6-yes:{0}", reconstructURI));
            reconstructURI = ribbonLoadBalancerClient.reconstructURI(instance1, new URI("https:/producer/test?name=123"));
            logger.info(MessageFormat.format("reconstructURI7-yes:{0}", reconstructURI));
        } catch (URISyntaxException e) {
            logger.error("访问异常", e);
        }
    }

    /**
     * 使用ribbon获取真实URL后使用noBalanceRestTemplate进行访问
     * @return
     * @throws URISyntaxException
     */
    @RequestMapping("/ribbon/complex/test")
    public ResponseEntity<CommResultMsg> test05() throws URISyntaxException {
        ServiceInstance serviceInstance = loadBalancerClient.choose("spring-cloud-producer");
        URI uri = loadBalancerClient.reconstructURI(serviceInstance, new URI("/producer/test?name=123"));
        ResponseEntity<CommResultMsg> forEntity = noBalanceRestTemplate.getForEntity(uri, CommResultMsg.class);
        try {
            String contextPath = this.request.getRequestURL().toString();
            forEntity.getBody().setObj(contextPath);
        } catch (Exception e) {
            logger.error("设置错误");
        }
        return forEntity;
    }
}
